package com.avo.ramcache.config;

import com.avo.ramcache.anno.core.Cached;
import com.avo.ramcache.anno.core.Index;
import com.avo.ramcache.anno.core.InitialConfig;
import com.avo.ramcache.anno.core.Unique;
import com.avo.ramcache.anno.core.type.CacheUnit;
import com.avo.ramcache.util.ReflectionUtil;
import org.apache.commons.collections.bidimap.DualHashBidiMap;

import java.io.Serializable;
import java.lang.reflect.Field;
import java.lang.reflect.Modifier;
import java.util.*;
import java.util.concurrent.locks.ReentrantReadWriteLock;

@SuppressWarnings({"rawtypes", "unchecked"})
public class CachedEntityConfig implements Serializable {

    private Class<? extends IEntity<?>> clz;
    private Cached cached;
    private InitialConfig initialConfig;
    private int cachedSize;
    private transient Map<String, Unique> uniques;
    private transient Map<String, Field> uniqueFields;
    private transient Map<String, ReentrantReadWriteLock> uniqueLocks;
    private transient Map<String, Index> indexs;
    private transient Map<String, Field> indexFields;
    private transient Map<String, ReentrantReadWriteLock> indexLocks;

    public static boolean isValid(Class<IEntity<?>> clz, Map<String, Integer> constants) {
        if (Modifier.isAbstract(clz.getModifiers())) {
            return false;
        }
        if (Modifier.isInterface(clz.getModifiers())) {
            return false;
        }
        if (!IEntity.class.isAssignableFrom(clz)) {
            return false;
        }
        if (!clz.isAnnotationPresent(Cached.class)) {
            return false;
        }
        Cached cached = clz.getAnnotation(Cached.class);
        if (!constants.containsKey(cached.size())) {
            throw new RuntimeException("缓存实体" + clz.getName() + "要求的缓存数量定义" + cached.size() + "不存在");
        }
        switch (cached.unit()) {
            case ENTITY:
                if (ReflectionUtil.getDeclaredFieldsWith(clz, Index.class).length > 0) {
                    throw new RuntimeException("缓存单位" + clz.getName() + "的实体" + cached.size() + "不支持索引属性配置");
                }
                break;
            case REGION:
                if (ReflectionUtil.getDeclaredFieldsWith(clz, Unique.class).length > 0) {
                    throw new RuntimeException("缓存单位" + clz.getName() + "的实体" + cached.size() + "不支持索引属性配置");
                }
                break;
            default:
                throw new RuntimeException("缓存实体" + clz.getName() + "使用了未支持的缓存单位" + cached.size() + "配置");
        }
        return true;
    }

    public static CachedEntityConfig valueOf(Class<IEntity<?>> clz, Map<String, Integer> constants) {
        CachedEntityConfig result = new CachedEntityConfig();
        result.clz = clz;
        result.cached = clz.getAnnotation(Cached.class);
        result.initialConfig = clz.getAnnotation(InitialConfig.class);
        result.cachedSize = constants.get(result.cached.size());
        setUniquesProperties(clz, result);
        setIndexProperties(clz, result);
        return result;
    }

    private static void setIndexProperties(Class<IEntity<?>> clz, CachedEntityConfig result) {
        Field[] fields = ReflectionUtil.getDeclaredFieldsWith(clz, Index.class);
        if (fields.length > 0) {
            HashMap indexs = new HashMap(fields.length);
            HashMap indexFields = new HashMap(fields.length);
            HashMap indexLocks = new HashMap(fields.length);
            for (Field field : fields) {
                Index index = field.getAnnotation(Index.class);
                ReflectionUtil.makeAccessible(field);
                indexs.put(field.getName(), index);
                indexFields.put(field.getName(), field);
                indexLocks.put(field.getName(), new ReentrantReadWriteLock());
            }
            result.indexs = indexs;
            result.indexFields = indexFields;
            result.indexLocks = indexLocks;
        }
    }

    private static void setUniquesProperties(Class<IEntity<?>> clz, CachedEntityConfig result) {
        Field[] fields = ReflectionUtil.getDeclaredFieldsWith(clz, Unique.class);
        if (fields.length > 0) {
            HashMap<String, Unique> indexs = new HashMap<>(fields.length);
            HashMap<String, Field> indexFields = new HashMap<>(fields.length);
            HashMap<String, ReentrantReadWriteLock> indexLocks = new HashMap<>(fields.length);
            for (Field field : fields) {
                Unique unique = field.getAnnotation(Unique.class);
                ReflectionUtil.makeAccessible(field);
                indexs.put(field.getName(), unique);
                indexFields.put(field.getName(), field);
                indexLocks.put(field.getName(), new ReentrantReadWriteLock());
            }
            result.uniques = indexs;
            result.uniqueFields = indexFields;
            result.uniqueLocks = indexLocks;
        }
    }

    public Collection<String> getIndexName() {
        if (this.indexs == null) {
            return Collections.EMPTY_SET;
        }
        HashSet<String> result = new HashSet<>(this.indexs.size());
        result.addAll(this.indexs.keySet());
        return result;
    }

    public String getIndexQuery(String name) {
        if (this.indexs == null) {
            throw new RuntimeException("实体" + clz.getName() + "的索引属性名" + cached.size() + "无效");
        }
        Index index = this.indexs.get(name);
        if (index == null) {
            throw new RuntimeException("实体" + clz.getName() + "的索引属性名" + cached.size() + "无效");
        }
        return index.query();
    }

    public Map<String, Object> getIndexValues(IEntity<?> entity) {
        if (this.uniqueFields == null) {
            throw new RuntimeException("实体" + this.clz.getName() + "没有唯一属性配置无法获取唯一属性值");
        }
        try {
            HashMap<String, Object> result = new HashMap<>(this.indexFields.size());
            for (Map.Entry<String, Field> entry : this.indexFields.entrySet()) {
                Object value = entry.getValue().get(entity);
                result.put(entry.getKey(), value);
            }
            return result;
        } catch (IllegalAccessException e) {
            throw new RuntimeException("无法获取唯一属性值" + e.getMessage());
        }
    }

    public Object getIndexValue(String name, IEntity<?> entity) {
        Map<String, Object> values = this.getIndexValues(entity);
        if (values.containsKey(name)) {
            return values.get(name);
        }
        throw new RuntimeException("索引属性" + name + "不存在");
    }

    public Object getIndexReadLock(String name) {
        ReentrantReadWriteLock lock = this.indexLocks.get(name);
        if (lock == null) {
            throw new RuntimeException("实体" + clz.getName() + "的索引属性名" + name + "无效");
        }
        return lock.readLock();
    }

    public Object getIndexWriteLock(String name) {
        ReentrantReadWriteLock lock = this.indexLocks.get(name);
        if (lock == null) {
            throw new RuntimeException("实体" + clz.getName() + "的索引属性名" + name + "无效");
        }
        return lock.writeLock();
    }

    public boolean hasIndexField(String name) {
        if (this.indexFields == null) {
            return false;
        }
        return indexFields.containsKey(name);
    }

    public String getUniqueQuery(String name) {
        if (this.uniques == null) {
            throw new RuntimeException("实体" + clz.getName() + "的索引属性名" + cached.size() + "无效");
        }
        Unique unique = this.uniques.get(name);
        if (unique == null) {
            throw new RuntimeException("实体" + clz.getName() + "的索引属性名" + cached.size() + "无效");
        }
        return unique.query();
    }

    public ReentrantReadWriteLock.ReadLock getUniqueReadLock(String name) {
        ReentrantReadWriteLock lock = this.uniqueLocks.get(name);
        if (lock == null) {
            throw new RuntimeException("实体" + clz.getName() + "的索引属性名" + name + "无效");
        }
        return lock.readLock();
    }

    public ReentrantReadWriteLock.WriteLock getUniqueWriteLock(String name) {
        ReentrantReadWriteLock lock = this.uniqueLocks.get(name);
        if (lock == null) {
            throw new RuntimeException("实体" + clz.getName() + "的索引属性名" + name + "无效");
        }
        return lock.writeLock();
    }

    public HashMap<String, DualHashBidiMap> buildUniqueCache() {
        HashMap<String, DualHashBidiMap> result = new HashMap<>(this.uniqueFields.size());
        for (String name : this.uniqueFields.keySet()) {
            DualHashBidiMap map = new DualHashBidiMap();
            result.put(name, map);
        }
        return result;
    }

    public Map<String, Object> getUniqueValues(IEntity<?> entity) {
        if (this.uniqueFields == null) {
            throw new RuntimeException("实体" + this.clz.getName() + "没有唯一属性配置无法获取唯一属性值");
        }
        try {
            HashMap<String, Object> result = new HashMap<>(this.uniqueFields.size());
            for (Map.Entry<String, Field> entry : this.uniqueFields.entrySet()) {
                Object value = entry.getValue().get(entity);
                result.put(entry.getKey(), value);
            }
            return result;
        } catch (IllegalAccessException e) {
            throw new RuntimeException("无法获取唯一属性值" + e.getMessage());
        }
    }

    public boolean hasUniqueField() {
        return this.uniqueFields != null;
    }

    public boolean cacheUnitIs(CacheUnit unit) {
        return cached.unit().equals(unit);
    }

    public Class<? extends IEntity<?>> getClz() {
        return clz;
    }

    public Cached getCached() {
        return this.cached;
    }

    public InitialConfig getInitialConfig() {
        return this.initialConfig;
    }

    public int getCachedSize() {
        return this.cachedSize;
    }

    public String getPersisterName() {
        return cached.persister().value();
    }

}
